#include "game.h"
#include "game_render_group.h"

#define CUTSCENE_WARMUP_TIME 2.0f

void RenderLayeredScene(game_assets* Assets, render_group* RenderGroup, loaded_bitmap* DrawBuffer,
                        layered_scene* Scene, real32 tNormal) {
    real32 SceneFadeValue = 1.0f;
    if (tNormal < Scene->tFadeIn) {
        SceneFadeValue = ClampMapToRange(0.0f, tNormal, Scene->tFadeIn);
    }
    v4 Color = {SceneFadeValue, SceneFadeValue, SceneFadeValue, 1.0f};

    v3 CameraStart = Scene->CameraStart;
    v3 CameraEnd = Scene->CameraEnd;
    v3 CameraOffset = Lerp(CameraStart, tNormal, CameraEnd);
    camera_params CameraParams = GetStandardCameraParams(DrawBuffer->Width, 0.25f);
    if (RenderGroup) {
        Perspective(RenderGroup, CameraParams.MetersToPixels, CameraParams.FocalLength, 0.0f);
    }
    hero_bitmaps_id HeroBitmaps = {};
    (void)HeroBitmaps;
    asset_vector MatchVector = {};
    asset_vector WeightVector = {};
    WeightVector.E[Tag_ShotIndex] = 10.0f;
    WeightVector.E[Tag_LayerIndex] = 1.0f;

    MatchVector.E[Tag_ShotIndex] = (real32)Scene->ShotIndex;
    if (Scene->LayerCount == 0) {
        Clear(RenderGroup, v4{0.0f, 0.0f, 0.0f, 0.0f});
    }
    // clang-format off
    // clang-format on
    for (uint32 LayerIndex = 1; LayerIndex <= Scene->LayerCount; ++LayerIndex) {
        scene_layer Layer = Scene->Layers[LayerIndex - 1];
        bool32 Active = true;
        if (Layer.Flags & SceneLayerFlag_Transient) {
            Active = ((tNormal >= Layer.Param.x) && (tNormal < Layer.Param.y));
        }
        if (Active) {
            MatchVector.E[Tag_LayerIndex] = (real32)LayerIndex;
            bitmap_id LayerImage =
                GetBestMatchBitmapFrom(Assets, Scene->AssetType, &MatchVector, &WeightVector);
            if (RenderGroup) {
                object_transform Transform = DefaultFlatTransform();

                v3 P = Layer.P;
                if (Layer.Flags & SceneLayerFlag_AtInfinity) {
                    P.z += CameraOffset.z;
                }
                if (Layer.Flags & SceneLayerFlag_CounterCameraX) {
                    Transform.OffsetP.x = P.x + CameraOffset.x;
                } else {
                    Transform.OffsetP.x = P.x - CameraOffset.x;
                }
                if (Layer.Flags & SceneLayerFlag_CounterCameraY) {
                    Transform.OffsetP.y = P.y + CameraOffset.y;
                } else {
                    Transform.OffsetP.y = P.y - CameraOffset.y;
                }

                if (Layer.Flags & SceneLayerFlag_Floaty) {
                    P.y += Layer.Param.x * Sin(Layer.Param.y * tNormal);
                }

                Transform.OffsetP.z = P.z - CameraOffset.z;
                PushBitmap(RenderGroup, &Transform, LayerImage, Layer.Height, v3{0, 0, 0}, Color);
            } else {
                PrefetchBitmap(Assets, LayerImage);
            }
        }
    }
}

real32 ShotChangeTime = 0.5f;

global_variable scene_layer IntroLayers1[] = {
    {{0.0f, 0.0f, 200.0f}, 300.0f, SceneLayerFlag_AtInfinity},  // 1
    {{0.0f, 0.0f, -170.0f}, 300.0f},                            // 奇怪的天空光线        // 2
    {{0.0f, 0.0f, -80.0f}, 40.0f},                              // 最远一排树           // 3
    {{0.0f, 10.0f, -70.0f}, 80.0f},                             // 中间山丘和树木      // 4
    {{0.0f, 0.0f, -50.0f}, 70.0f},                              // 前方的山丘和树木     // 5
    {{0.0f, 0.0f, -30.0f}, 50.0f},                              // 6
    {{0.0f, -2.0f, -20.0f}, 50.0f},                             // 7
    {{0.0f, 0.0f, -5.0f}, 25.0f},                               //
};

global_variable scene_layer IntroLayers2[] = {
    {{2.0f, -5.0f, -60.0f}, 102.0f},  // 主角与树 // 1
    {{0.0f, 0.0f, -18.0f}, 30.0f},    // 墙与窗   // 2
    {{0.0f, 2.0f, -8.0f}, 10.0f},  // 冰柱 / 冰锥（通常指冬天屋檐上垂下来的冰挂）   // 3
};

global_variable scene_layer IntroLayers3[] = {
    {{0.0f, 0.0f, -30.0f}, 100.0f, SceneLayerFlag_AtInfinity},     // 天空
    {{0.0f, 0.0f, -20.0f}, 45.0f, SceneLayerFlag_CounterCameraY},  // 外面的森林
    {{0.0f, -2.0f, -4.0f}, 15.0f, SceneLayerFlag_CounterCameraY},
    {{0.0f, 0.35f, -0.5f}, 1.0f},  //
};

global_variable scene_layer IntroLayers4[] = {
    {{0.0f, 0.0f, -4.0f}, 6.0f},                                                      //
    {{-1.2f, -0.2f, -4.0f}, 4.0f, SceneLayerFlag_Transient, {0.0f, ShotChangeTime}},  //
    {{-1.2f, -0.2f, -4.0f}, 4.0f, SceneLayerFlag_Transient, {ShotChangeTime, 1.0f}},  //
    {{2.25f, -1.5f, -3.0f}, 2.0f},                                                    //
    {{0.0f, 0.35f, -1.0f}, 1.0f},                                                     //
};

global_variable scene_layer IntroLayers5[] = {
    {{0.0f, 0.0f, -20.0f}, 30.0f},
    {{0.0f, 0.0f, -5.0f}, 8.0f, SceneLayerFlag_Transient, {0.0f, ShotChangeTime}},
    {{0.0f, 0.0f, -5.0f}, 8.0f, SceneLayerFlag_Transient, {ShotChangeTime, 1.0f}},
    {{0.0f, 0.0f, -3.0f}, 4.0f, SceneLayerFlag_Transient, {ShotChangeTime, 1.0f}},
    {{0.0f, 0.0f, -2.0f}, 3.0f, SceneLayerFlag_Transient, {ShotChangeTime, 1.0f}},
};

global_variable scene_layer IntroLayers6[] = {
    {{0.0f, 0.0f, -8.0f}, 12.0f},   {{0.0f, 0.0f, -5.0f}, 8.0f},   {{1.0f, -1.0f, -3.0f}, 3.0f},
    {{0.85f, -0.95f, -3.0f}, 0.5f}, {{-2.0f, -1.0f, -2.5f}, 2.0f}, {{0.2f, 0.5f, -1.0f}, 1.0f},
};

global_variable scene_layer IntroLayers7[] = {
    {{-0.5f, 0.0f, -8.0f}, 12.0f, SceneLayerFlag_CounterCameraX},
    {{-1.0f, 0.0f, -4.0f}, 6.0f},
};

global_variable scene_layer IntroLayers8[] = {
    {{0.0f, 0.0f, -8.0f}, 12.0f},
    {{0.0f, -1.0f, -5.0f}, 4.0f, SceneLayerFlag_Floaty, {0.05f, 15.0f}},
    {{3.0f, -1.5f, -3.0f}, 2.0f},
    {{0.0f, 0.0f, -1.5f}, 2.5f},
};

global_variable scene_layer IntroLayers9[] = {
    {{0.0f, 0.0f, -8.0f}, 12.0f},
    {{0.0f, 0.25f, -3.0f}, 4.0f},
    {{1.0f, 0.0f, -2.0f}, 3.0f},
    {{1.0f, 0.1f, -1.0f}, 2.0f},
};

global_variable scene_layer IntroLayers10[] = {
    {{-15.0f, 25.0f, -100.0f}, 130.0f, SceneLayerFlag_AtInfinity},
    {{0.0f, 0.0f, -10.0f}, 22.0f},
    {{-0.8f, -0.2f, -3.0f}, 4.5f},
    {{0.0f, 0.0f, -2.0f}, 4.5f},
    {{0.0f, -0.25f, -1.0f}, 1.5f},
    {{0.2f, 0.2f, -0.5f}, 1.0f},
};

global_variable scene_layer IntroLayers11[] = {
    {{0.0f, 0.0f, -100.0f}, 150.0f, SceneLayerFlag_AtInfinity},
    {{0.0f, 10.0f, -40.0f}, 40.0f},
    {{0.0f, 3.2f, -20.0f}, 23.0f},
    {{0.25f, 0.9f, -10.0f}, 13.5f},
    {{-0.5f, 0.625f, -5.0f}, 7.0f},
    {{0.0f, 0.1f, -2.5f}, 3.9f},
    {{-0.3f, -0.15f, -1.0f}, 1.2f},
};

#define INTRO_SCENE(Index) \
    Asset_OpeningCutscene, Index, ArrayCount(IntroLayers##Index), IntroLayers##Index
global_variable layered_scene IntroCutScene[] = {
    //
    {Asset_None, 0, 0, 0, CUTSCENE_WARMUP_TIME},
    //
    {INTRO_SCENE(1), 20.0f, {0.0f, 0.0f, 10.0f}, {-3.0f, -2.0, -5.0f}, 0.1f},
    // 分镜2
    {INTRO_SCENE(2), 10.0f, {0.0f, 0.0f, 0.0f}, {-0.5f, -0.5f, -1.0f}},
    // 分镜3
    {INTRO_SCENE(3), 20.0f, {0.0f, 0.0f, 0.0f}, {0.0f, 6.5f, -1.5f}},
    // 分镜4
    {INTRO_SCENE(4), 20.0f, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, -0.5f}},
    // 分镜5
    {INTRO_SCENE(5), 20.0f, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, -0.5f}},
    // 分镜6
    {INTRO_SCENE(6), 20.0f, {0.0f, 0.0f, 0.0f}, {-0.5f, 0.5f, -1.0f}},
    // 分镜7
    {INTRO_SCENE(7), 20.0f, {0.0f, 0.0f, 0.0f}, {2.0f, 0.5f, -1.0f}},
    // 分镜8
    {INTRO_SCENE(8), 20.0f, {0.0f, 0.0f, 0.0f}, {0.0f, -0.5f, -1.0f}},
    // 分镜9
    {INTRO_SCENE(9), 20.0f, {0.0f, 0.0f, 0.0f}, {-0.5f, 0.5f, -1.0f}},
    // 分镜10
    {INTRO_SCENE(10), 20.0f, {0.0f, 0.0f, 0.0f}, {-0.5f, 0.5f, -1.0f}},
    // 分镜11
    {INTRO_SCENE(11), 20.0f, {0.0f, 0.0f, 0.0f}, {-0.5f, 0.5f, -2.0f}}
    //
};

struct cutscene {
    uint32 SceneCount;
    layered_scene* Scenes;
};

global_variable cutscene CutScenes[] = {
    {ArrayCount(IntroCutScene), IntroCutScene},
};

internal bool32 RenderCutsceneAtTime(game_assets* Assets, render_group* RenderGroup,
                                     loaded_bitmap* DrawBuffer, game_mode_cutscene* CutScene,
                                     real32 tCutScene) {
    real32 tBase = 0.0f;
    bool32 CutsceneStillRunning = false;
    cutscene Info = CutScenes[CutScene->ID];
    for (uint32 ShotIndex = 0; ShotIndex < Info.SceneCount; ShotIndex++) {
        layered_scene* Scene = Info.Scenes + ShotIndex;
        real32 tStart = tBase;
        real32 tEnd = tStart + Scene->Duration;
        if ((tCutScene >= tStart) && (tCutScene < tEnd)) {
            real32 tNormal = ClampMapToRange(tStart, tCutScene, tEnd);
            RenderLayeredScene(Assets, RenderGroup, DrawBuffer, &IntroCutScene[ShotIndex], tNormal);
            CutsceneStillRunning = true;
        }
        tBase = tEnd;
    }
    return CutsceneStillRunning;
}

bool32 CheckForMetaInput(game_state* GameState, transient_state* TranState, game_input* Input) {
    bool32 Result = false;
    for (uint32 ControllerIndex = 0; ControllerIndex < ArrayCount(Input->Controllers);
         ++ControllerIndex) {
        game_controller_input* Controller = GetController(Input, ControllerIndex);
        if (WasPressed(Controller->Back)) {
            Input->QuitRequested = true;
            break;
        } else if (WasPressed(Controller->Start)) {
            PlayWorld(GameState, TranState);
            Result = true;
            break;
        }
    }
    return Result;
}

bool32 UpdateAndRenderTitleScreen(game_state* GameState, transient_state* TranState,
                                  render_group* RenderGroup, loaded_bitmap* DrawBuffer,
                                  game_mode_title_screen* TitleScreen,  //
                                  game_input* Input) {
    (void)RenderGroup;
    (void)DrawBuffer;
    (void)TitleScreen;
    bool32 Result = CheckForMetaInput(GameState, TranState, Input);
    if (!Result) {
        Clear(RenderGroup, v4{1.0f, 0.25f, 0.25f, 0.0f});
        if (TitleScreen->t > 10.0f) {
            PlayIntroCutscene(GameState, TranState);
        } else {
            TitleScreen->t += 2 * Input->dtForFrame;
        }
    }
    return Result;
}

bool32 UpdateAndRenderCutscene(game_state* GameState, transient_state* TranState,
                               render_group* RenderGroup, loaded_bitmap* DrawBuffer,
                               game_input* Input, game_mode_cutscene* CutScene) {
    game_assets* Assets = TranState->Assets;
    bool32 Result = CheckForMetaInput(GameState, TranState, Input);
    if (!Result) {
        RenderCutsceneAtTime(Assets, 0, DrawBuffer, CutScene, CutScene->t + CUTSCENE_WARMUP_TIME);
        bool32 CutsceneStillRunning =
            RenderCutsceneAtTime(Assets, RenderGroup, DrawBuffer, CutScene, CutScene->t);
        if (CutsceneStillRunning) {
            CutScene->t += Input->dtForFrame;
        } else {
            PlayTitleScreen(GameState, TranState);
        }
    }
    return Result;
}

void PlayIntroCutscene(game_state* GameState, transient_state* TranState) {
    SetGameMode(GameState, TranState, GameMode_CutScene);
    game_mode_cutscene* Result = PushStruct(&GameState->ModeArena, game_mode_cutscene);
    Result->ID = CutsceneID_Intro;
    Result->t = 0;
    GameState->Cutscene = Result;
}

void PlayTitleScreen(game_state* GameState, transient_state* TranState) {
    SetGameMode(GameState, TranState, GameMode_TitleScreen);
    game_mode_title_screen* Result = PushStruct(&GameState->ModeArena, game_mode_title_screen);
    Result->t = 0;
    GameState->TitleScreen = Result;
}